/*************************************************************
*
* CAMTRANS.CPP
*
* Transformations related to camera
*************************************************************/
#include <m3d/m3d_stdafx.h>

#include <ap_comm.h>
#include <m3d.h>
#include <camera.h>

/*************************************************************
* camCAMERA::TransformPoly()
*
* IN : poly     - polygon to be transformed
* OUT: polyCam  - transformed polygon
*
* Transform polycam to camera space
*************************************************************/
void camCAMERA::TransformPoly (const m3dPOLY *poly, m3dPOLY *polyCam) const
{
   int i;

   for (i = 0; i < m3dPolyNVert(poly); i++ )  {
      TransformPoint(m3dPolyVertex(poly,i), m3dPolyVertex(polyCam,i));
   }

   m3dPolyNVert(polyCam) = m3dPolyNVert(poly);

   return;
}

/*************************************************************
* camCAMERA::ProjPoly ()
*
* IN:  poly    - polygon in 3D, to be projected
* OUT: poly2D  - projected polygon
*
* Project polygon onto the near clipping plane (perspective projection)
* Polygon SHOULD lie between clipping planes
*************************************************************/
void camCAMERA::ProjPoly (m3dPOLY *poly, m2dPOLY *poly2D)
{
   int    i;
   float  ooz;

   for (i = 0; i < m3dPolyNVert(poly); i++ )  {
      // check that the point lies farther than the near clipping plane
      ASSERT(m3dPolyVertexZ(poly,i) > M3D_EPSILON);

      ooz = nearPlane/m3dPolyVertexZ(poly,i);
      m3dPolyVertex(poly2D,i)->x = m3dPolyVertexX(poly,i)*ooz;
      m3dPolyVertex(poly2D,i)->y = m3dPolyVertexY(poly,i)*ooz;
   }

   m3dPolyNVert(poly2D) = m3dPolyNVert(poly);
   return;
}

/*************************************************************
* camCAMERA::ProjPoint()
*
* IN:  pV3D    - point to be projected
* OUT: pV2D    - projected point
*
* Project a point in perspective projection
*************************************************************/
void camCAMERA::ProjPoint (m3dV *pV3D, m2dV *pV2D)
{
   // check that the point lies farther then the near clipping plane
   ASSERT(pV3D->z > M3D_EPSILON);
   pV2D->x = pV3D->x*nearPlane/pV3D->z;
   pV2D->y = pV3D->y*nearPlane/pV3D->z;

   return;
}

/*************************************************************
* camCAMERA::_ProjPoint()
*
* IN:  pV3D    - point to be projected
* OUT: pV2D    - projected point
*
* Same as ProjPoint, but ASSERT-less
*************************************************************/
BOOL camCAMERA::_ProjPoint (m3dV *pV3D, m2dV *pV2D)
{
   // avoid divide by zero
   if (m3dFAbs(pV3D->z) < M3D_EPSILON) {
      pV2D->x = pV3D->x;
      pV2D->y = pV3D->y;
   } else {
      pV2D->x = pV3D->x*nearPlane/pV3D->z;
      pV2D->y = pV3D->y*nearPlane/pV3D->z;
   }

   // check that the point lies farther then the near clipping plane
   return (pV3D->z > M3D_EPSILON);
}

/*************************************************************
* camCAMERA::BackProjPoly()
*
* IN:  poly2D      - poly in image plane (to be back-projected)
*      polyPlane3D - original poly3D that after projection/clipping
*                    became poly2D
*      isBackface  - is polygon backfaced?
* OUT: poly3D      - back-projected polygon
*
* RET: TRUE  - function managed to backproject the polygon
*      FALSE - function failed
*
* NOTE: Normally a function never fails if we use planar
*       polygons. In case of non-planar polygons this function
*       may fail, that in turn causes many fails in rendering
*       pipeline. To avoid this non-planar polygons must
*       be thrown out of the normal rendering if they cannot
*       be backprojected properly
*************************************************************/
BOOL camCAMERA::BackProjPoly (m2dPOLY   *poly2D, m3dPOLY *polyPlane3DCCS,
                              m3dPOLY   *poly3DCCS,
                              m3dV      *normPolyCCS, 
                              BOOL       isBackface)
{
   BOOL  f;
   m3dV  rayOrg, rayDir;
   m3dV  normPolyCCSInv;
   int   i;

   // ray origin lies in the origin of viewer coord system
   rayOrg.x = rayOrg.y = rayOrg.z = 0.f;

   if (isBackface) {
      m3dScaleVector(normPolyCCS, -1.f, &normPolyCCSInv);
      normPolyCCS = &normPolyCCSInv;
   }

   rayDir.z = nearPlane;
   for (i = 0; i < m3dPolyNVert(poly2D); i++)  {
      rayDir.x = m3dPolyVertexX(poly2D,i);
      rayDir.y = m3dPolyVertexY(poly2D,i);

      f = m3dIsectRayPlane(&rayOrg, &rayDir, polyPlane3DCCS, m3dPolyVertex(poly3DCCS,i), normPolyCCS);

      if (!f)  {
         return FALSE;
      }
   }

   m3dPolyNVert(poly3DCCS) = m3dPolyNVert(poly2D);

   return TRUE;
}

/*************************************************************
* camCAMERA::ProjPolyOrtho()
*
* IN:  poly   - polygon to be projected
* OUT: poly2D - pojected polygon
*
* Project polygon onto the near clipping plane using orthographic
* projection
* Polygon is expected to lie in front of the 
* camera origin (Z>0 in CCS)
*************************************************************/
void camCAMERA::ProjPolyOrtho (m3dPOLY *poly, m2dPOLY *poly2D)
{
   int    i;

   for (i = 0; i < m3dPolyNVert(poly); i++ )  {
      // check that the point lies farther then the near clipping plane
      ASSERT(m3dPolyVertex(poly,i)->z > -M3D_EPSILON);
      m3dPolyVertex(poly2D,i)->x = m3dPolyVertexX(poly,i);
      m3dPolyVertex(poly2D,i)->y = m3dPolyVertexY(poly,i);
   }

   m3dPolyNVert(poly2D) = m3dPolyNVert(poly);

   return;
}

/*************************************************************
* camCAMERA::ProjPointOrtho()
*
* Project a point in orthographic projection
*************************************************************/
void camCAMERA::ProjPointOrtho (m3dV *pV3D, m2dV *pV2D)
{
   // check that the point lies farther then the near clipping plane
   ASSERT(pV3D->z > M3D_EPSILON);
   pV2D->x = pV3D->x;
   pV2D->y = pV3D->y;

   return;
}

/*************************************************************
* camCAMERA::BackProjPolyOrtho ()
*
* This function should become a member as well
*************************************************************/
BOOL camCAMERA::BackProjPolyOrtho (m2dPOLY *poly2D,    m3dPOLY *polyPlane3DCCS,
                                   m3dPOLY *poly3DCCS, m3dV    *normPolyCCS,    BOOL isBackface)
{
   BOOL  f;
   m3dV  rayOrg;
   m3dV  normPolyCCSInv;
   int   i;

   if (isBackface) {
      m3dScaleVector(normPolyCCS, -1.f, &normPolyCCSInv);
      normPolyCCS = &normPolyCCSInv;
   }

   for (i = 0; i < m3dPolyNVert(poly2D); i++ )  {
      // ray origin is a back projected point
      rayOrg.x = m3dPolyVertexX(poly2D,i);
      rayOrg.y = m3dPolyVertexY(poly2D,i);
      rayOrg.z = 0.f;

      // ray direction is Z-axis
      f = m3dIsectRayPlane(&rayOrg, &m3dVUnitZ, polyPlane3DCCS, m3dPolyVertex(poly3DCCS,i), normPolyCCS);

      if (!f)  {
         return FALSE;
      }
   }

   m3dPolyNVert(poly3DCCS) = m3dPolyNVert(poly2D);

   return TRUE;
}

//
// End-of-file CAMTRANS.CPP
//
